Skip to content

ENH: Controller Implementation Refactor#1065

Draft
MateusStano wants to merge 15 commits into
enh/eventsfrom
enh/control-refactor
Draft

ENH: Controller Implementation Refactor#1065
MateusStano wants to merge 15 commits into
enh/eventsfrom
enh/control-refactor

Conversation

@MateusStano

Copy link
Copy Markdown
Member

TBD

MateusStano and others added 15 commits June 22, 2026 21:55
- _ControllerPrints read the removed 'interactive_objects' attribute, so
  controller.info()/all_info() raised AttributeError; renamed to
  controlled_objects() reading controller.controlled_objects.
- Event docstring and event_usage.rst claimed context keys are unpacked
  into the callback kwargs, but Event.__call__ never does that; access is
  via kwargs['event'].context.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… tracking

A Controller is now a public Event subclass (periodic, trigger-less,
changes_dynamics, priority 3) instead of a private wrapper around one.
This removes the mirrored enabled/log/context state and fixes the
context-lifecycle bug where the dict mutated by the controller function
diverged from the event context reset between flights.

- After every execution the controller snapshots the control_state of
  each controlled object into control_history; recorded_schedule exposes
  it as Functions of time for prints/plots and open-loop replay.
- New ControlledObject protocol (control_state/set_control/_reset), not
  surface-specific so future TVC actuators can conform.
- ControllableGenericSurface gains initial_control_state and _reset();
  AirBrakes inherits them. Controller.reset() restores every tracked
  object, generalizing the airbrakes-only reset previously in Flight.
- Positional controller_function signatures are now rejected with a
  migration error; the add_air_brakes legacy shim is the only remaining
  compatibility path (DeprecationWarning).
- _Controller removed from the public API; new Flight.controllers
  property; new _ControllerPlots.control_history plot and richer
  controller prints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers Event integration, callback kwargs injection, positional-signature
rejection, automatic control-state history and recorded_schedule
interpolation, context/history/object reset semantics, controlled-object
rebinding, and to_dict/from_dict round trips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…llable_surface

- SurfaceController(Controller) validates its controlled objects are
  ControllableGenericSurface instances and exposes .surfaces; tracking
  and reset stay in the base so future non-surface controllers (TVC)
  need no changes here.
- AirBrakesController(SurfaceController) pins the 'air_brakes' friendly
  name, exposes .air_brakes and .deployment_level_history (Function of
  time), and ships deployment-focused prints/plots.
- Rocket.add_air_brakes now builds an AirBrakesController; pure **kwargs
  controller functions are passed through unwrapped, hybrid and legacy
  positional signatures keep the (deprecation-warned) wrapper.
- New Rocket.add_controllable_surface wires surface(s) into
  aerodynamic_surfaces and registers a SurfaceController, resolving the
  previously dangling docstring references.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Air brakes now join rocket.aerodynamic_surfaces and are summed in the
standard generic-surface loop of all u_dot variants, instead of the
dedicated drag-only branch in _aerodynamic_drag_force.

- add_air_brakes gains a position parameter. Without it, the force
  application point is pinned to the center of dry mass (zero moment
  arm), matching historical results; the pin survives add_motor because
  __evaluate_single_surface_cp_to_cdm short-circuits on the sentinel.
  With an explicit position, deployed brakes produce proper moments at
  angle of attack.
- The airbrake cD is now 0 at deployment level 0 unconditionally;
  override_rocket_drag became a body-drag suppression branch (body drag
  applies while brakes are retracted, is replaced by the surface-loop
  airbrake drag while deployed).
- Result note: drag is now decomposed along the freestream like every
  other surface, adding small lateral components at nonzero incidence
  (previously pure axial).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- AirBrakes surface: deployment-0 => cD 0 (incl. override mode), curve
  passthrough, incidence/rate independence, clamp on/off parity with the
  integration tests, reset protocol, serialization round trip.
- Rocket wiring: surface-loop membership, cp pinned to CDM surviving
  add_motor, explicit-position path, static-margin invariance, legacy
  signature deprecations, add_controllable_surface wiring (with surface
  validation moved before add_surfaces for a clear error).
- Derivatives without Flight: body-drag suppression semantics and force
  equivalence of the surface-loop path with the legacy drag formula.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New ScheduledController replays a control schedule open-loop via
  set_control (clamping applies); ScheduledController.from_controller
  rebuilds a flight's control inputs from a recorded control_history.
- Controller.to_dict now records needs and the controlled objects' names
  (controlled_objects_ref), plus the control_history under
  include_outputs; from_dict restores them, and falls back to a
  ScheduledController (with a warning) when the pickled controller
  function cannot be decoded but a history is available.
- Rocket.from_dict rewires controllers to the decoded surfaces by name,
  replacing the dead _interactive_objects_hash path that raised
  AttributeError on every load of a rocket with controllers.
- Air brakes are serialized once, inside aerodynamic_surfaces (the
  air_brakes key is dropped from Rocket.to_dict and rebuilt on load,
  preserving instance identity with the controller); legacy files with
  only the air_brakes key still load, pinned to the CDM. The pin flag
  round-trips via AirBrakes.to_dict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…texts

- Per-sample controllers are rebuilt as AirBrakesController with a deep
  copy of the base context, so nested mutables (observed-variable lists)
  are no longer shared across Monte Carlo samples.
- Sampled air brakes are wired through Rocket._attach_air_brakes so they
  join the aerodynamic surface loop (a plain list append would leave
  them with zero drag under the unified force path); add_air_brakes
  accepts an optional position forwarded to each sample.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- airbrakes.rst: kwargs controller contract, deprecation of positional
  signatures and initial_observed_variables, position parameter,
  automatic deployment tracking and plots, replay section.
- event_usage.rst: note that controllers are Event subclasses.
- Reference stubs for Controller, SurfaceController,
  AirBrakesController and ScheduledController.
- CHANGELOG entries for the controller refactor (also includes the
  pre-existing uncommitted entries for the reduced-rates change and the
  center_of_pressure removal from earlier branch work).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolve ScheduledController dynamically in the from_dict fallback so the
module graph stays acyclic (scheduled_controller already imports
controller at top level).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Introduce Effector/GenericEffector and Rocket.add_effector: a non-aerodynamic
control path that injects a body-frame force and/or moment directly into the
equations of motion, driven by the existing Controller.

- New rocketpy/rocket/effector package (Effector base implementing the
  ControlledObject protocol + GenericEffector with user force/moment callables).
- Rocket gains an effectors registry, effectors_cp_to_cdm moment arms,
  add_effector(...) (optionally wiring a Controller), and save/load support;
  effectors are excluded from the aerodynamic-surface / stability-margin math.
- flight_derivatives sums effector force+moment in u_dot and u_dot_generalized
  (force only in 3-DOF); guarded so effector-free flights are unchanged.
- Exports plus unit tests and a slow roll-damper integration test.

Also fix compute_needs_union so a controller/event that self-enables via
enable_on while declaring needs gets its expensive kwargs on the enabling node.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@MateusStano MateusStano self-assigned this Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant